﻿#pragma once

#include "../../Shared/Enums.h"
#include "../MessageQueue.h"
#include "../MirEnvir/Envir.h"
#include "../MirEnvir/Map.h"
#include "../../Shared/Data/Stat.h"
#include "MonsterObject.h"
#include "PlayerObject.h"
#include "DelayedAction.h"
#include "../../Shared/Packet.h"
#include "../../Shared/Data/ClientData.h"
#include <string>
#include <unordered_map>
#include <vector>
#include <cmath>
#include <limits>
#include <any>

//C# TO C++ CONVERTER NOTE: Forward class declarations:
namespace Server::MirObjects { class Buff; }
namespace Server::MirObjects { class Poison; }

using namespace Server::MirDatabase;
using namespace Server::MirEnvir;
using namespace Server::MirObjects::Monsters;
namespace S = ServerPackets;

namespace Server::MirObjects
{
    class MapObject
    {
    private:
        int PKPoints = 0;
        AttackMode AMode = static_cast<AttackMode>(0);
        PetMode PMode = static_cast<PetMode>(0);

    public:
        virtual ~MapObject()
        {
            delete _currentMap;
            delete NameColour;
            delete _target;
            delete Master;
            delete LastHitter;
            delete EXPOwner;
            delete Owner;
            delete Stats;
            delete Node;
            delete NodeThreaded;
        }

    protected:
        static Server::MessageQueue *getMessageQueue();

        static Server::MirEnvir::Envir *getEnvir();

    public:
        const unsigned int ObjectID = getEnvir()->getObjectID();

        virtual ObjectType getRace() const = 0;

        virtual std::string getName() const = 0;
        virtual void setName(const std::string &value) = 0;

        long long ExplosionInflictedTime = 0;
        int ExplosionInflictedStage = 0;

    private:
        int SpawnThread = 0;

        Map *_currentMap;
    public:
        void setCurrentMap(Map *value);
        Map *getCurrentMap() const;

        virtual int getCurrentMapIndex() const = 0;
        virtual void setCurrentMapIndex(int value) = 0;
        virtual Point *getCurrentLocation() const = 0;
        virtual void setCurrentLocation(Point *value) = 0;
        virtual MirDirection getDirection() const = 0;
        virtual void setDirection(MirDirection value) = 0;

        virtual unsigned short getLevel() const = 0;
        virtual void setLevel(unsigned short value) = 0;

        virtual int getHealth() const = 0;
        virtual int getMaxHealth() const = 0;
        unsigned char getPercentHealth() const;

        unsigned char Light = 0;
        int AttackSpeed = 0;

        int CurrentHandWeight = 0, CurrentWearWeight = 0, CurrentBagWeight = 0;

        long long CellTime = 0, BrownTime = 0, PKPointTime = 0, LastHitTime = 0, EXPOwnerTime = 0;
        Color *NameColour = Color::White;

        bool Dead = false, Undead = false, Harvested = false, AutoRev = false;

        std::vector<KeyValuePair<std::string, std::string>*> NPCVar = std::vector<KeyValuePair<std::string, std::string>*>();

        virtual int getPKPoints() const;
        virtual void setPKPoints(int value);

        unsigned short PotHealthAmount = 0, PotManaAmount = 0, HealAmount = 0, VampAmount = 0;

        bool CoolEye = false;
    private:
        bool _hidden = false;

    public:
        bool getHidden() const;
        void setHidden(bool value);

    private:
        bool _observer = false;
    public:
        bool getObserver() const;
        void setObserver(bool value);

        #pragma region Sneaking
    private:
        bool _sneakingActive = false;
    public:
        bool getSneakingActive() const;
        void setSneakingActive(bool value);

    private:
        bool _sneaking = false;
    public:
        bool getSneaking() const;
        void setSneaking(bool value);
        #pragma endregion

        MapObject *_target;
        virtual MapObject *getTarget() const;
        virtual void setTarget(MapObject *value);


        MapObject *Master, *LastHitter, *EXPOwner, *Owner;
        long long ExpireTime = 0, OwnerTime = 0, OperateTime = 0;
        int OperateDelay = 100;

        Stats *Stats;

        std::vector<MonsterObject*> Pets = std::vector<MonsterObject*>();
        std::vector<Buff*> Buffs = std::vector<Buff*>();

        std::vector<PlayerObject*> GroupMembers;

        virtual AttackMode getAMode() const;
        virtual void setAMode(AttackMode value);

        virtual PetMode getPMode() const;
        virtual void setPMode(PetMode value);
        bool InSafeZone = false;

        float ArmourRate = 0, DamageRate = 0; //recieved not given

        std::vector<Poison*> PoisonList = std::vector<Poison*>();
        PoisonType CurrentPoison = PoisonType::None;
        std::vector<DelayedAction*> ActionList = std::vector<DelayedAction*>();

        LinkedListNode<MapObject*> *Node;
        LinkedListNode<MapObject*> *NodeThreaded;
        long long RevTime = 0;

        virtual bool getBlocking() const;

        Point *getFront() const;

        Point *getBack() const;



        virtual void Process();

        virtual void SetOperateTime() = 0;

        int GetAttackPower(int min, int max);

        int GetRangeAttackPower(int min, int max, int range);

        int GetDefencePower(int min, int max);

        virtual void Remove(PlayerObject *player);
        virtual void Add(PlayerObject *player);
        virtual void Remove(MonsterObject *monster);
        virtual void Add(MonsterObject *monster);

        virtual void Process(DelayedAction *action) = 0;


        bool CanFly(Point *target);

        virtual void Spawned();
        virtual void Despawn();

        MapObject *FindObject(unsigned int targetID, int dist);


        virtual void Broadcast(Packet *p);

        virtual void BroadcastInfo();

        virtual bool IsAttackTarget(PlayerObject *attacker) = 0;
        virtual bool IsAttackTarget(MonsterObject *attacker) = 0;
        virtual int Attacked(PlayerObject *attacker, int damage, DefenceType type = DefenceType::ACAgility, bool damageWeapon = true) = 0;
        virtual int Attacked(MonsterObject *attacker, int damage, DefenceType type = DefenceType::ACAgility) = 0;

        virtual int GetArmour(DefenceType type, MapObject *attacker, bool &hit);

        virtual void ApplyNegativeEffects(PlayerObject *attacker, DefenceType type, unsigned short levelOffset);

        virtual int Struck(int damage, DefenceType type = DefenceType::ACAgility) = 0;

        virtual bool IsFriendlyTarget(PlayerObject *ally) = 0;
        virtual bool IsFriendlyTarget(MonsterObject *ally) = 0;

        virtual void ReceiveChat(const std::string &text, ChatType type) = 0;

        virtual Packet *GetInfo() = 0;

        virtual void WinExp(unsigned int amount, unsigned int targetLevel = 0);

        virtual bool CanGainGold(unsigned int gold);
        virtual void WinGold(unsigned int gold);

        virtual bool Harvest(PlayerObject *player);

        virtual void ApplyPoison(Poison *p, MapObject *Caster = nullptr, bool NoResist = false, bool ignoreDefence = true) = 0;

        virtual Buff *AddBuff(BuffType type, MapObject *owner, int duration, Stats *stats, bool visible = false, bool infinite = false, bool stackable = false, bool refreshStats = true, std::vector<int> &values);

        virtual void RemoveBuff(BuffType b);

        bool HasBuff(BuffType type, Buff *&buff);

        bool HasAnyBuffs(BuffType exceptBuff, std::vector<BuffType> &types);

    protected:
        void HideFromTargets();

    public:
        bool CheckStacked();

        virtual bool Teleport(Map *temp, Point *location, bool effects = true, unsigned char effectnumber = 0);

        virtual bool TeleportRandom(int attempts, int distance, Map *map = nullptr);

        Point *GetRandomPoint(int attempts, int distance, Map *map);

        void BroadcastHealthChange();

        void BroadcastDamageIndicator(DamageType type, int damage = 0);

        virtual void Die() = 0;
        virtual int Pushed(MapObject *pusher, MirDirection dir, int distance) = 0;

        bool IsMember(MapObject *member);

        virtual void SendHealth(PlayerObject *player) = 0;

        void setInTrapRock(bool value);
        bool getInTrapRock() const;

    };

    class Poison
    {
    public:
        MapObject *Owner;
        PoisonType PType = static_cast<PoisonType>(0);
        int Value = 0;
        long long Duration = 0, Time = 0, TickTime = 0, TickSpeed = 0;

        virtual ~Poison()
        {
            delete Owner;
        }

        Poison();

        Poison(BinaryReader *reader);
    };

    class Buff
    {
    private:
        std::unordered_map<std::string, std::any> Data = std::unordered_map<std::string, std::any>();

    public:
        virtual ~Buff()
        {
            delete Caster;
            delete Stats;
        }

    private:
        std::unordered_map<std::string, std::any> getData() const;
        void setData(const std::unordered_map<std::string, std::any> &value);

    public:
        BuffType Type = static_cast<BuffType>(0);
        MapObject *Caster;
        bool Visible = false;
        unsigned int ObjectID = 0;
        long long ExpireTime = 0;

        Stats *Stats;

        std::vector<int> Values;
        bool Infinite = false;
        bool Stackable = false;

        bool RealTime = false;
        DateTime RealTimeExpire;

        bool Paused = false;

        Buff();

        Buff(BinaryReader *reader);

        void Save(BinaryWriter *writer);

        template<typename T>
        T Get(const std::string &key)
        {
            Object result;
            std::unordered_map<std::string, std::any>::const_iterator Data_iterator = Data.find(key);
            if (getData()_iterator == getData().end())
            {
                result = Data_iterator->second;
                return T();
            }
            else
            {
                result = Data_iterator->second;
            }

            return static_cast<T>(result);
        }

        void Set(const std::string &key, std::any val);

        ClientBuff *ToClientBuff();
    };
}
